﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using ASPNETPerformance.App_BusinessLogic.Helpers;
using ASPNETPerformance.App_BusinessLogic.Utils;
using Yahoo.Yui.Compressor;

namespace ASPNETPerformance.App_BusinessLogic.JsAndCss {
    public class JsAndCssManager {
        public static void Execute( string root ) {
            if ( Directory.Exists( root ) ) {
                var path = string.Empty;
                var jsRoot = root + "\\js";
                var cssRoot = root + "\\css";

                foreach ( var file in Directory.GetFiles( jsRoot, "*.js" ) ) {
                    MinCompressByJs( jsRoot, file, out path );
                    if ( !string.IsNullOrEmpty( path ) ) {
                        GzipCompress( jsRoot, path );
                    }
                }

                foreach ( var file in Directory.GetFiles( cssRoot, "*.css" ) ) {
                    MinCompressByCss( cssRoot, file, out path );
                    if ( CommonHelper.GetCookieBool( "IsBustImageByCss" ) ) {
                        BustImageByCss( path );
                    }
                    if ( !string.IsNullOrEmpty( path ) ) {
                        GzipCompress( cssRoot, path );
                    }
                }
            }
        }

        private static readonly Regex _stylesheetPattern = new Regex( @"\('?(?<file>.*?\.(gif|jpg|png))(\?.*?)?'?\)", RegexOptions.Compiled | RegexOptions.ExplicitCapture );
        private static void BustImageByCss( string path ) {
            var content = File.ReadAllText( path );
            content = _stylesheetPattern.Replace( content, new MatchEvaluator( ApplyVersionIdToImageByCss ) );
            File.WriteAllText( path, content );
        }

        private static string ApplyVersionIdToImageByCss( Match match ) {
            var target = match.Groups[ 1 ].Value;
            string path = target.StartsWith( "../" ) ?
                HttpContext.Current.Server.MapPath( match.Groups[ 1 ].Value.Replace( "../", "~/_assets/" ) ) :
                HttpContext.Current.Server.MapPath( "~" + match.Groups[ 1 ].Value );
            return string.Format( "('{0}?{1}')", match.Groups[ 1 ].Value, SEO.SEOHelper.LastUpdateTimeHex( path ) );
        }

        private static void MinCompressByCss( string root, string file, out string path ) {
            string outputMinDirectory = root + "\\Release\\min\\";
            path = outputMinDirectory + Path.GetFileName( file ).Replace( ".min", "" ).Replace( ".css", "" ) + ".min.css";
            using ( var sw = new StreamWriter( path ) )
            using ( var sr = new StreamReader( file ) ) {
                var compressed = CssCompressor.Compress( sr.ReadToEnd() );
                sw.Write( compressed );
            }
        }

        private static void MinCompressByJs( string root, string file, out string path ) {
            string outputMinDirectory = root + "\\Release\\min\\";
            path = outputMinDirectory + Path.GetFileName( file ).Replace( ".min", "" ).Replace( ".js", "" ) + ".min.js";
            using ( var sw = new StreamWriter( path ) )
            using ( var sr = new StreamReader( file ) ) {
                var compressed = JavaScriptCompressor.Compress( sr.ReadToEnd(), false );
                sw.Write( compressed );
            }
        }

        private static void GzipCompress( string root, string file ) {
            string outputGzipDirectory = root + "\\Release\\zip\\";
            var name = Path.GetFileName( file );
            using ( var input = File.OpenRead( file ) )
            using ( var output = File.Create( outputGzipDirectory + name + ".gz" ) )
            using ( var compress = new GZipStream( output, CompressionMode.Compress ) ) {
                var buffer = new byte[ 8192 ];
                int numRead;
                while ( ( numRead = input.Read( buffer, 0, buffer.Length ) ) != 0 ) {
                    compress.Write( buffer, 0, numRead );
                }
            }
        }

        public static void ExecuteCombineByJs( string rootPath, string[] inputs, string combineName ) {
            ExecuteCombine( rootPath, inputs, combineName, "js" );
        }

        public static void ExecuteCombineByCss( string rootPath, string[] inputs, string combineName ) {
            ExecuteCombine( rootPath, inputs, combineName, "css" );
        }

        private static void ExecuteCombine( string rootPath, string[] inputs, string combineName, string sprPath ) {
            rootPath = rootPath.EndsWith( "\\" ) ? rootPath : string.Concat( rootPath, '\\' );

            using ( var sw = new StreamWriter( string.Format( rootPath + "_assets\\{0}\\" + combineName.Trim(), sprPath ), false ) ) {
                foreach ( var input in inputs ) {
                    sw.WriteLine( File.ReadAllText( rootPath + input.Trim() ) );
                }
            }
        }
    }
}